Ontsluit het volledige potentieel van Pandas door aangepaste functies te beheersen. Deze gids behandelt verschillen, prestaties en gebruiksscenario's.
Pandas Beheersen: Een Diepe Duik in Aangepaste Functies met apply(), map(), en applymap()
In de wereld van data science en analyse is de Python Pandas-bibliotheek een onmisbaar hulpmiddel. Het biedt krachtige, flexibele en efficiënte datastructuren die ontworpen zijn om het werken met gestructureerde gegevens zowel eenvoudig als intuïtief te maken. Hoewel Pandas een rijke set ingebouwde functies voor aggregatie, filtering en transformatie biedt, komt er een tijd in de reis van elke dataprofessional dat deze niet volstaan. U moet uw eigen aangepaste logica, een unieke bedrijfsregel of een complexe transformatie toepassen die niet direct beschikbaar is.
Dit is waar het vermogen om aangepaste functies toe te passen een superkracht wordt. Pandas biedt echter verschillende manieren om dit te bereiken, voornamelijk via de apply(), map() en applymap() methoden. Voor nieuwkomers kunnen deze functies verwarrend vergelijkbaar lijken. Welke moet u gebruiken? Wanneer? En wat zijn de prestatie-implicaties van uw keuze?
Deze uitgebreide gids zal deze krachtige methoden demystificeren. We zullen elk in detail onderzoeken, hun specifieke gebruiksscenario's begrijpen en, nog belangrijker, leren hoe we het juiste gereedschap voor de klus kunnen kiezen om schone, efficiƫnte en leesbare Pandas-code te schrijven. We behandelen:
- De
map()methode: Ideaal voor element-wise transformatie op een enkele Series. - De
apply()methode: Het veelzijdige werkpaard voor rij-wise of kolom-wise bewerkingen op een DataFrame. - De
applymap()methode: De specialist voor element-wise bewerkingen over een heel DataFrame. - Prestatieoverwegingen: Het cruciale verschil tussen deze methoden en echte vectorisatie.
- Best Practices: Een beslissingskader om u te helpen telkens weer de meest efficiƫnte methode te kiezen.
Het Toneel Klaarzetten: Onze Sample Dataset
Om onze voorbeelden praktisch en duidelijk te maken, werken we met een consistente, wereldwijd relevante dataset. We maken een voorbeeld DataFrame dat online verkoopgegevens van een fictief internationaal e-commercebedrijf vertegenwoordigt.
import pandas as pd
import numpy as np
data = {
'OrderID': [1001, 1002, 1003, 1004, 1005, 1006, 1007, 1008],
'Product': ['Laptop', 'Mouse', 'Keyboard', 'Monitor', 'Webcam', 'Headphones', 'Docking Station', 'Mouse'],
'Category': ['Electronics', 'Accessories', 'Accessories', 'Electronics', 'Accessories', 'Audio', 'Electronics', 'Accessories'],
'Price_USD': [1200, 25, 75, 300, 50, 150, 250, 30],
'Quantity': [1, 2, 1, 2, 1, 1, 1, 3],
'Country': ['USA', 'Canada', 'USA', 'Germany', 'Japan', 'Canada', 'Germany', np.nan]
}
df = pd.DataFrame(data)
print(df)
Dit DataFrame biedt ons een mooie mix van gegevenstypen (numeriek, tekenreeks en zelfs een ontbrekende waarde) om de volledige mogelijkheden van onze doel functies te demonstreren.
De `map()` Methode: Element-wise Transformatie voor een Series
Wat is `map()`?
De map() methode is uw gespecialiseerde hulpmiddel voor het wijzigen van waarden binnen een enkele kolom (een Pandas Series). Het werkt element-voor-element. Zie het als zeggen: "Voor elk item in deze kolom, zoek het op in een woordenboek of stuur het door deze functie en vervang het door het resultaat."
Het wordt voornamelijk gebruikt voor twee taken:
- Waarden vervangen op basis van een woordenboek (een mapping).
- Een eenvoudige functie toepassen op elk element.
Gebruiksscenario 1: Waarden Mappen met een Woordenboek
Dit is het meest voorkomende en efficiƫnte gebruik van map(). Stel dat we een bredere 'Afdeling' kolom willen maken op basis van onze 'Categorie' kolom. We kunnen een mapping definiƫren in een Python woordenboek en map() gebruiken om deze toe te passen.
category_to_department = {
'Electronics': 'Technology',
'Accessories': 'Peripherals',
'Audio': 'Technology'
}
df['Department'] = df['Category'].map(category_to_department)
print(df[['Category', 'Department']])
Uitvoer:
Category Department
0 Electronics Technology
1 Accessories Peripherals
2 Accessories Peripherals
3 Electronics Technology
4 Accessories Peripherals
5 Audio Technology
6 Electronics Technology
7 Accessories Peripherals
Merk op hoe elegant dit werkt. Elke waarde in de 'Categorie' Series wordt opgezocht in het `category_to_department` woordenboek, en de bijbehorende waarde wordt gebruikt om de nieuwe 'Department' kolom te vullen. Als een sleutel niet in het woordenboek wordt gevonden, zal map() een NaN (Not a Number) waarde produceren, wat vaak het gewenste gedrag is voor niet-gemapte categorieƫn.
Gebruiksscenario 2: Een Functie Toepassen met `map()`
U kunt ook een functie (inclusief een lambda-functie) doorgeven aan map(). De functie wordt uitgevoerd voor elk element in de Series. Laten we een nieuwe kolom maken die ons een beschrijvend label geeft voor de prijs.
def price_label(price):
if price > 200:
return 'High-Value'
elif price > 50:
return 'Mid-Value'
else:
return 'Low-Value'
df['Price_Label'] = df['Price_USD'].map(price_label)
# Gebruik van een lambda functie voor een eenvoudigere taak:
# df['Product_Length'] = df['Product'].map(lambda x: len(x))
print(df[['Product', 'Price_USD', 'Price_Label']])
Uitvoer:
Product Price_USD Price_Label
0 Laptop 1200 High-Value
1 Mouse 25 Low-Value
2 Keyboard 75 Mid-Value
3 Monitor 300 High-Value
4 Webcam 50 Low-Value
5 Headphones 150 Mid-Value
6 Docking Station 250 High-Value
7 Mouse 30 Low-Value
Wanneer `map()` te Gebruiken: Een Snelle Samenvatting
- U werkt aan een enkele kolom (een Series).
- U moet waarden vervangen op basis van een woordenboek of een andere Series. Dit is de belangrijkste kracht.
- U moet een eenvoudige element-wise functie toepassen op een enkele kolom.
De `apply()` Methode: Het Veelzijdige Werkpaard
Wat is `apply()`?
Als map() een specialist is, is apply() de algemene krachtpatser. Het is flexibeler omdat het kan werken op zowel Series als DataFrames. De sleutel tot het begrijpen van apply() is de axis parameter, die de bewerking stuurt:
- Op een Series: Het werkt element-voor-element, veel zoals
map(). - Op een DataFrame met
axis=0(de standaard): Het past een functie toe op elke kolom. De functie ontvangt elke kolom als een Series. - Op een DataFrame met
axis=1: Het past een functie toe op elke rij. De functie ontvangt elke rij als een Series.
`apply()` op een Series
Wanneer gebruikt op een Series, gedraagt apply() zich erg vergelijkbaar met map(). Het past een functie toe op elk element. We zouden bijvoorbeeld ons prijs label voorbeeld kunnen repliceren.
df['Price_Label_apply'] = df['Price_USD'].apply(price_label)
print(df['Price_Label_apply'].equals(df['Price_Label'])) # Uitvoer: True
Hoewel ze hier uitwisselbaar lijken, is map() vaak iets sneller voor eenvoudige woordenboek substituties en element-wise bewerkingen op een Series omdat het een meer geoptimaliseerd pad heeft voor die specifieke taken.
`apply()` op een DataFrame (Kolom-wise, `axis=0`)
Dit is de standaardmodus voor een DataFrame. De functie die u opgeeft, wordt ƩƩn keer aangeroepen voor elke kolom. Dit is nuttig voor kolom-wise aggregaties of transformaties.
Laten we het verschil vinden tussen de maximum- en minimumwaarde (het bereik) voor elk van onze numerieke kolommen.
numeric_cols = df[['Price_USD', 'Quantity']]
def get_range(column_series):
return column_series.max() - column_series.min()
column_ranges = numeric_cols.apply(get_range, axis=0)
print(column_ranges)
Uitvoer:
Price_USD 1175.0
Quantity 2.0
dtype: float64
Hier ontving de get_range functie eerst de 'Price_USD' Series, berekende het bereik ervan, ontving vervolgens de 'Quantity' Series en deed hetzelfde, waarbij een nieuwe Series met de resultaten werd teruggegeven.
`apply()` op een DataFrame (Rij-wise, `axis=1`)
Dit is wellicht het krachtigste en meest voorkomende gebruiksscenario voor apply(). Wanneer u een nieuwe waarde moet berekenen op basis van meerdere kolommen in dezelfde rij, is apply() met axis=1 uw go-to oplossing.
De functie die u doorgeeft, ontvangt elke rij als een Series, waarbij de index de kolomnamen zijn. Laten we de totale kosten voor elke bestelling berekenen.
def calculate_total_cost(row):
# 'row' is een Series die een enkele rij vertegenwoordigt
price = row['Price_USD']
quantity = row['Quantity']
return price * quantity
df['Total_Cost'] = df.apply(calculate_total_cost, axis=1)
print(df[['Product', 'Price_USD', 'Quantity', 'Total_Cost']])
Uitvoer:
Product Price_USD Quantity Total_Cost
0 Laptop 1200 1 1200
1 Mouse 25 2 50
2 Keyboard 75 1 75
3 Monitor 300 2 600
4 Webcam 50 1 50
5 Headphones 150 1 150
6 Docking Station 250 1 250
7 Mouse 30 3 90
Dit is iets wat map() simpelweg niet kan doen, omdat het beperkt is tot een enkele kolom. Laten we een complexer voorbeeld bekijken. We willen de verzendprioriteit van elke bestelling categoriseren op basis van zijn categorie en land.
def assign_shipping_priority(row):
if row['Category'] == 'Electronics' and row['Country'] == 'USA':
return 'High Priority'
elif row['Total_Cost'] > 500:
return 'High Priority'
elif row['Country'] == 'Japan':
return 'Medium Priority'
else:
return 'Standard'
df['Shipping_Priority'] = df.apply(assign_shipping_priority, axis=1)
print(df[['Category', 'Country', 'Total_Cost', 'Shipping_Priority']])
Wanneer `apply()` te Gebruiken: Een Snelle Samenvatting
- Wanneer uw logica afhankelijk is van meerdere kolommen in een rij (gebruik
axis=1). Dit is het belangrijkste kenmerk. - Wanneer u een aggregatiefunctie moet toepassen over kolommen of rijen.
- Als een algemeen functie-applicatie hulpmiddel wanneer
map()niet past.
Een Speciale Vermelding: De `applymap()` Methode
Wat is `applymap()`?
De applymap() methode is een andere specialist, maar zijn domein is het hele DataFrame. Het past een functie toe op elk individueel element van een DataFrame. Het werkt niet op een Seriesāhet is een DataFrame-exclusieve methode.
Zie het als het uitvoeren van een map() op elke kolom tegelijkertijd. Het is nuttig voor brede, ingrijpende transformaties, zoals opmaak of typeconversie, over alle cellen.
DataFrame.applymap() uitgefaseerd. De nieuwe aanbevolen manier is om DataFrame.map() te gebruiken. De functionaliteit is hetzelfde. We gebruiken hier applymap() voor compatibiliteit, maar wees u bewust van deze wijziging voor toekomstige code.
Een Praktisch Voorbeeld
Laten we zeggen dat we een sub-DataFrame hebben met alleen onze numerieke kolommen en we willen ze allemaal formatteren als valuta-strings voor een rapport.
numeric_df = df[['Price_USD', 'Quantity', 'Total_Cost']]
# Gebruik van een lambda functie om elk getal te formatteren
formatted_df = numeric_df.applymap(lambda x: f'${x:,.2f}')
print(formatted_df)
Uitvoer:
Price_USD Quantity Total_Cost 0 $1,200.00 $1.00 $1,200.00 1 $25.00 $2.00 $50.00 2 $75.00 $1.00 $75.00 3 $300.00 $2.00 $600.00 4 $50.00 $1.00 $50.00 5 $150.00 $1.00 $150.00 6 $250.00 $1.00 $250.00 7 $30.00 $3.00 $90.00
Een ander veelvoorkomend gebruik is het opschonen van een DataFrame met tekenreekgegevens door bijvoorbeeld alles naar kleine letters te converteren.
string_df = df[['Product', 'Category', 'Country']].copy() # Maak een kopie om SettingWithCopyWarning te voorkomen
# Zorg ervoor dat alle waarden tekenreeksen zijn om fouten te voorkomen
string_df = string_df.astype(str)
lower_df = string_df.applymap(str.lower)
print(lower_df)
Wanneer `applymap()` te Gebruiken: Een Snelle Samenvatting
- Wanneer u een enkele, eenvoudige functie wilt toepassen op elk element in een DataFrame.
- Voor taken zoals dataconditionering, tekenreekopmaak of eenvoudige wiskundige transformaties over het hele DataFrame.
- Onthoud de uitfasering ten gunste van
DataFrame.map()in recente Pandas-versies.
Prestatie Diepe Duik: Vectorisatie vs. Iteratie
De "Verborgen" Lus
Dit is het meest cruciale concept om te begrijpen voor het schrijven van hoog-presterende Pandas-code. Hoewel apply(), map() en applymap() handig zijn, zijn het in wezen slechts chique wrappers rond een Python-lus. Wanneer u df.apply(..., axis=1) gebruikt, itereert Pandas rij voor rij door uw DataFrame en geeft elke rij door aan uw functie. Dit proces heeft aanzienlijke overhead en is veel langzamer dan bewerkingen die in C of Cython zijn geoptimaliseerd.
De Kracht van Vectorisatie
Vectorisatie is de praktijk van het uitvoeren van bewerkingen op hele arrays (of Series) tegelijk, in plaats van op individuele elementen. Pandas en zijn onderliggende bibliotheek, NumPy, zijn specifiek ontworpen om ongelooflijk snel te zijn bij gevectoriseerde bewerkingen.
Laten we onze 'Total_Cost' berekening herhalen. We gebruikten apply(), maar is er een gevectoriseerde manier?
# Methode 1: Gebruik van apply() (Iteratie)
df['Total_Cost'] = df.apply(lambda row: row['Price_USD'] * row['Quantity'], axis=1)
# Methode 2: Gevectoriseerde Bewerking
df['Total_Cost_Vect'] = df['Price_USD'] * df['Quantity']
# Controleer of de resultaten hetzelfde zijn
print(df['Total_Cost'].equals(df['Total_Cost_Vect'])) # Uitvoer: True
De tweede methode is gevectoriseerd. Het neemt de hele 'Price_USD' Series en vermenigvuldigt deze met de hele 'Quantity' Series in ƩƩn, zeer geoptimaliseerde bewerking. Als u deze twee methoden op een groot DataFrame (miljoenen rijen) zou timen, zou de gevectoriseerde aanpak niet alleen sneller zijnāhet zou orde van grootte sneller zijn. We hebben het over seconden versus minuten, of minuten versus uren.
Wanneer is `apply()` Onvermijdelijk?
Als vectorisatie zo veel sneller is, waarom bestaan deze andere methoden dan? Omdat uw logica soms te complex is om te worden gevectoriseerd. apply() is het noodzakelijke en correcte hulpmiddel wanneer:
- Complexe Voorwaardelijke Logica: Uw logica omvat ingewikkelde
if/elif/elsestatements die afhankelijk zijn van meerdere kolommen, zoals onsassign_shipping_priorityvoorbeeld. Hoewel een deel hiervan kan worden bereikt metnp.select(), kan het onleesbaar worden. - Externe Bibliotheek Functies: U moet een functie uit een externe bibliotheek toepassen op uw gegevens. Bijvoorbeeld, het toepassen van een functie uit een geo-spatiƫle bibliotheek om de afstand te berekenen op basis van lengte- en breedtegraadkolommen, of een functie uit een natuurlijke taalverwerkingsbibliotheek (zoals NLTK) om sentimentanalyse uit te voeren op een tekstkolom.
- Iteratieve Processen: De berekening voor een bepaalde rij is afhankelijk van een waarde die in een vorige rij is berekend (hoewel dit zeldzaam is en vaak een teken dat een andere datastructuur nodig is).
Best Practice: Vectoriseer Eerst, `apply()` Daarna
Dit leidt tot de gouden regel van Pandas-prestaties:
Zoek altijd eerst naar een gevectoriseerde oplossing. Gebruik `apply()` als uw krachtige, flexibele vangnet wanneer een gevectoriseerde oplossing niet praktisch of mogelijk is.
Samenvatting en Belangrijkste Conclusies: Het Juiste Gereedschap Kiezen
Laten we onze kennis consolideren in een duidelijk beslissingskader. Wanneer u wordt geconfronteerd met een aangepaste transformatietaak, stel uzelf dan deze vragen:
Vergelijkingstabel
| Methode | Werkt op | Scope van Bewerking | Functie Ontvangt | Primair Gebruiksscenario |
|---|---|---|---|---|
| Vectorisatie | Series, DataFrame | Hele array tegelijk | N.v.t. (bewerking is direct) | Rekenkundige, logische bewerkingen. Hoogste Prestaties. |
.map() |
Alleen Series | Element-voor-element | Een enkel element | Waarden vervangen uit een woordenboek. |
.apply() |
Series, DataFrame | Rij-voor-rij of Kolom-voor-kolom | Een Series (een rij of kolom) | Complexe logica met meerdere kolommen per rij. |
.applymap() |
Alleen DataFrame | Element-voor-element | Een enkel element | Opmaak of transformatie van elke cel in een DataFrame. |
Een Beslissingsflowchart
- Kan mijn bewerking worden uitgedrukt met behulp van basis rekenkundige (+, -, *, /) of logische operatoren (&, |, ~) op hele kolommen?
→ Ja? Gebruik een gevectoriseerde aanpak. Dit is het snelst. (bijv.df['col1'] * df['col2']) - Werk ik alleen aan een enkele kolom, en is mijn belangrijkste doel het vervangen van waarden op basis van een woordenboek?
→ Ja? GebruikSeries.map(). Het is hiervoor geoptimaliseerd. - Moet ik een functie toepassen op elk individueel element in mijn hele DataFrame?
→ Ja? GebruikDataFrame.applymap()(ofDataFrame.map()in nieuwere Pandas). - Is mijn logica complex en vereist het waarden uit meerdere kolommen in elke rij om een enkel resultaat te berekenen?
→ Ja? GebruikDataFrame.apply(..., axis=1). Dit is uw hulpmiddel voor complexe, rij-wise logica.
Conclusie
Het navigeren door de opties voor het toepassen van aangepaste functies in Pandas is een rite de passage voor elke data-beoefenaar. Hoewel ze op het eerste gezicht uitwisselbaar lijken, zijn map(), apply(), en applymap() verschillende hulpmiddelen, elk met zijn eigen sterke punten en ideale gebruiksscenario's. Door hun verschillen te begrijpen, kunt u code schrijven die niet alleen correct is, maar ook leesbaarder, onderhoudbaarder en significant performanter.
Onthoud de hiƫrarchie: geef de voorkeur aan vectorisatie vanwege zijn rauwe snelheid, gebruik map() voor zijn efficiƫnte Series-substitutie, kies applymap() voor DataFrame-brede transformaties, en maak gebruik van de kracht en flexibiliteit van apply() voor complexe rij-wise of kolom-wise logica die niet kan worden gevectoriseerd. Met deze kennis bent u nu beter uitgerust om elke data-manipulatie-uitdaging aan te gaan, ruwe gegevens met vaardigheid en efficiƫntie om te zetten in krachtige inzichten.